import itertools
from itertools import chain

import sqlparse
from sqlparse.sql import IdentifierList, Identifier, Function
from sqlparse.tokens import Keyword, DML

from common.documents import APIModel


def extract_update_part(parsed):
    into_seen = False
    get_tabled = False
    if parsed.is_group:
        for item in parsed.tokens:
            if into_seen:
                if item.ttype is Keyword:
                    if not get_tabled:
                        get_tabled = True
                        yield item
                    else:
                        into_seen = False
                elif item.is_whitespace:
                    continue
                elif isinstance(item, Identifier):
                    get_tabled = True
                    yield item
            if item.is_keyword and item.value.upper() in ['UPDATE']:
                into_seen = True
                get_tabled = False


def parse(content):
    """
    解析sql内感兴趣的内容
    :param content:
    :return: [表->语句类型]
    """
    parsed = sqlparse.parse(content)[0]
    stream = []
    if parsed.get_type() == 'UPDATE':
        stream = list(extract_update_part(parsed))
    if parsed.get_type() == 'INSERT':
        stream = list(extract_into_part(parsed))
    stream = list(extract_from_part(parsed)) + stream

    return parsed.get_type(), list(extract_table_identifiers(stream))


def get_data_by_route(data, route):
    td = data
    for i, r in enumerate(route):
        td = td[r]
    return td


def get_data_route(data):
    """获取参数的路由"""
    if isinstance(data, dict):
        for key in data:
            t = [key]
            t.extend(get_data_route(data[key]))
            yield t
    if isinstance(data, list):
        for i in range(len(data)):
            t = [i]
            t.extend(get_data_route(data[i]))
            yield t
    return []


def get_data_by_routes(data, routes, all_have=True):
    """

    :param data:
    :param routes:
    :param all_have: 所有的路由都要获取到
    :return:
    """
    res = type(data)()
    for route in routes:
        try:

            tr = res
            td = data
            for i, r in enumerate(route):
                if i < len(route) - 1:
                    tr[r] = type(td[r])()
                    tr = tr[r]
                    td = td[r]
                else:
                    tr[r] = td[r]
        except:
            if all_have:
                return None
    return res


def get_all_combinations(items, s_to_m=True):
    if s_to_m:
        ite = range(1, len(items))
    else:
        ite = range(len(items) - 1, 0, -1)
    for i in ite:
        yield from itertools.combinations(items, i)


def get_model_struct():
    # 获取模型的url结构
    res = {}
    for item in APIModel.objects():
        tr = res
        url = item.get_relate_url().split('/')
        if len(url) < 3:
            url += [''] * (3 - len(url))
        lv = 0
        for i in chain(url[:2], ['/'.join(url)]):
            lv += 1
            if lv == 3:
                tr[i] = item
            else:
                if i not in tr:
                    tr[i] = {}
            tr = tr[i]
    # 最高3级
    return res


def is_subselect(parsed):
    if not parsed.is_group:
        return False
    for item in parsed.tokens:
        if is_subselect(item):
            return True
        if item.ttype is DML and item.value.upper() == 'SELECT':
            return True

    return False


def extract_into_part(parsed):
    into_seen = False
    get_tabled = False
    if parsed.is_group:
        for item in parsed.tokens:
            if into_seen:
                if item.ttype is Keyword:
                    if not get_tabled:
                        get_tabled = True
                        yield item
                    else:
                        into_seen = False
                elif item.is_whitespace:
                    continue
                elif isinstance(item, Identifier):
                    get_tabled = True
                    yield item
                elif isinstance(item, Function):
                    for t in item.tokens:
                        if isinstance(t, Identifier):
                            get_tabled = True
                            yield t
            if item.ttype is Keyword and item.value.upper() in ['INTO']:
                into_seen = True
                get_tabled = False


def extract_from_part(parsed):
    from_seen = False
    get_from = True
    if parsed.is_group:
        for item in parsed.tokens:
            if is_subselect(item):
                if from_seen:
                    get_from = True
                yield from extract_from_part(item)
            else:
                if from_seen:
                    if item.ttype is Keyword:
                        if not get_from:
                            get_from = True
                            yield item  # 因为有些table和keyword是一样的，所以这里再返回第一个
                        else:
                            from_seen = False
                    elif item.is_whitespace:
                        continue
                    elif isinstance(item, Identifier):
                        get_from = True
                        yield item
                if item.ttype is Keyword and item.value.upper() in ['FROM'] or 'JOIN' in item.value.upper():
                    from_seen = True
                    get_from = False


def get_outer_identifiers_name(token_stream):
    yield from [item.get_name() for item in token_stream if isinstance(item, Identifier)]


def extract_table_identifiers(token_stream):
    for item in token_stream:
        if isinstance(item, IdentifierList):
            for identifier in item.get_identifiers():
                yield identifier.get_name()
        elif isinstance(item, Identifier):
            if len(item.tokens) > 1:
                yield from extract_table_identifiers(item.tokens)
            else:
                yield item.get_name()
        elif item.ttype == Keyword:
            yield item.value
        elif item.is_whitespace:
            raise StopIteration
        else:
            yield str(item)


def extract_definitions(token_list):
    # assumes that token_list is a parenthesis
    definitions = []
    tmp = []
    # grab the first token, ignoring whitespace. idx=1 to skip open (
    tidx, tkl = token_list.token_next(1)
    while tkl and not tkl.match(sqlparse.tokens.Punctuation, ')'):
        if isinstance(tkl, IdentifierList):
            tl = list(tkl)
        else:
            tl = [tkl]
        tidx, tkl = token_list.token_next(tidx, skip_ws=False)
        for token in tl:
            if token and token.match(sqlparse.tokens.Punctuation, ','):
                definitions.append(tmp)
                tmp = []
                continue
            if token.value.strip():
                tmp.append(token)
            # grab the next token, this times including whitespace
            # split on ",", except when on end of statement

    if tmp and isinstance(tmp[0], sqlparse.sql.Identifier):
        definitions.append(tmp)
    return definitions


if __name__ == '__main__':
    get_model_struct()
